package com.viknix.threadpool.manager.server.dependence.util;

import com.viknix.threadpool.manager.common.util.IOUtils;
import com.viknix.threadpool.manager.server.dependence.constant.Constants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.*;

import static com.viknix.threadpool.manager.server.dependence.constant.Commons.TPM_SERVER_CONTEXT;

/**
 * @Author: Dongqi
 * @Date: 2021/11/10 18:27
 * @Version 1.0
 * @Description: 环境变量工具类
 */
public class EnvUtil {

    public static final String STANDALONE_MODE_ALONE = "standalone";

    public static final String STANDALONE_MODE_CLUSTER = "cluster";

    /**
     * The key of tpm home.
     */
    public static final String TPM_HOME_KEY = "tpm.home";

    /**
     * 本机ip地址
     */
    private static String localAddress = "";

    /**
     * 端口号
     */
    private static int port = -1;

    /**
     * 上下文路径
     */
    private static String contextPath = null;

    /**
     * 环境配置类
     */
    private static ConfigurableEnvironment environment;

    /**
     * 是否是单机模式启动，默认为false
     */
    private static Boolean isStandalone = null;

    /**
     * 配置文件的路径
     */
    private static String confPath = "";


    public static ConfigurableEnvironment getEnvironment() {
        return environment;
    }

    public static void setEnvironment(ConfigurableEnvironment environment) {
        EnvUtil.environment = environment;
    }

    public static void setPort(int port) {
        EnvUtil.port = port;
    }

    /**
     * 将内容写入 cluster.conf 文件
     *
     * @param content
     * @throws IOException
     */
    public static void writeClusterConf(String content) throws IOException {
        DiskUtils.writeFile(new File(getClusterConfFilePath()), content.getBytes(StandardCharsets.UTF_8), false);
    }

    public static void setContextPath(String contextPath) {
        EnvUtil.contextPath = contextPath;
    }

    public static void setLocalAddress(String localAddress) {
        EnvUtil.localAddress = localAddress;
    }

    public static String getContextPath() {
        if (Objects.isNull(contextPath)) {
            contextPath = getProperty(Constants.WEB_CONTEXT_PATH, TPM_SERVER_CONTEXT);
            if (Constants.ROOT_WEB_CONTEXT_PATH.equals(contextPath)) {
                contextPath = StringUtils.EMPTY;
            }
        }
        return contextPath;
    }

    public static String getConfPath() {
        if (StringUtils.isNotBlank(EnvUtil.confPath)) {
            return EnvUtil.confPath;
        }
        EnvUtil.confPath = Paths.get(getTpmHome(), "conf").toString();
        return confPath;
    }

    public static int getPort() {
        if (port == -1) {
            port = getProperty("server.port", Integer.class, 8848);
        }
        return port;
    }

    public static String getClusterConfFilePath() {
        return Paths.get(getTpmHome(), "conf", "cluster.conf").toString();
    }

    public static String getTpmHome() {
        String tpmHome = System.getProperty(TPM_HOME_KEY);
        if (StringUtils.isBlank(tpmHome)) {
            tpmHome = Paths.get(System.getProperty("user.home"), "tpm").toString();
        }
        return tpmHome;
    }

    /**
     * 读取 cluster.conf 获取集群节点ip列表
     *
     * @return ip list.
     * @throws IOException ioexception {@link IOException}
     */
    public static List<String> readClusterConf() throws IOException {

        try (Reader reader = new InputStreamReader(new FileInputStream(new File(getClusterConfFilePath())),
                StandardCharsets.UTF_8)) {
            // 解析集群配置文件
            return analyzeClusterConf(reader);
        } catch (FileNotFoundException ignore) {
            ignore.printStackTrace();
            List<String> tmp = new ArrayList<>();
            String clusters = EnvUtil.getMemberList();
            if (StringUtils.isNotBlank(clusters)) {
                String[] details = clusters.split(",");
                for (String item : details) {
                    tmp.add(item.trim());
                }
            }
            return tmp;
        }
    }

    /**
     * 解析集群配置文件
     * 1、不用的机器之间可以写在不同的行上
     * 2、机器和机器可以用英文逗号隔开
     *
     * @param reader reader
     * @return 集群节点地址列表
     * @throws IOException IOException
     */
    public static List<String> analyzeClusterConf(Reader reader) throws IOException {

        List<String> instanceList = new ArrayList<String>();
        List<String> lines = IOUtils.readLines(reader);
        String comment = "#";
        for (String line : lines) {
            String instance = line.trim();
            if (instance.startsWith(comment)) {
                // 跳过注解
                continue;
            }
            // 如果单行中包含注解信息，截取有效部分配置信息
            if (instance.contains(comment)) {
                // 192.168.71.52:3180 # Instance A
                instance = instance.substring(0, instance.indexOf(comment));
                instance = instance.trim();
            }
            // 配置与配置之间可以用英文逗号隔开
            int multiIndex = instance.indexOf(Constants.COMMA_DIVISION);
            if (multiIndex > 0) {
                // support the format: ip1:port,ip2:port  # multi inline
                instanceList.addAll(Arrays.asList(instance.split(Constants.COMMA_DIVISION)));
            } else {
                //support the format: 192.168.71.52:8848
                instanceList.add(instance);
            }
        }
        return instanceList;
    }

    public static String getMemberList() {

        String val = null;
        if (environment == null) {
            val = System.getenv("tpm.member.list");
            if (StringUtils.isBlank(val)) {
                val = System.getProperty("tpm.member.list");
            }
        } else {
            val = getProperty("tpm.member.list");
        }
        return val;
    }

    /**
     * load resource to map.
     *
     * @param resource resource
     * @return Map&lt;String, Object&gt;
     * @throws IOException IOException
     */
    public static Map<String, ?> loadProperties(Resource resource) throws IOException {
        return new OriginTrackedPropertiesLoader(resource).load();
    }

    public static String getProperty(String key) {
        return environment.getProperty(key);
    }

    public static String getProperty(String key, String defaultValue) {
        return environment.getProperty(key, defaultValue);
    }

    public static <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
        return environment.getProperty(key, targetType, defaultValue);
    }

    /**
     * 是否单机模式启动，默认为false
     */
    public static boolean getStandaloneMode() {
        if (Objects.isNull(isStandalone)) {
            isStandalone = Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME);
        }
        return isStandalone;
    }

    public static List<String> getPropertyList(String key) {
        List<String> valueList = new ArrayList<>();

        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String value = environment.getProperty(key + "[" + i + "]");
            if (org.apache.commons.lang3.StringUtils.isBlank(value)) {
                break;
            }

            valueList.add(value);
        }

        return valueList;
    }

    private static final String FILE_PREFIX = "file:";

    public static Resource getApplicationConfFileResource() {
        Resource customResource = getCustomFileResource();
        return customResource == null ? getDefaultResource() : customResource;
    }

    private static Resource getCustomFileResource() {
        String path = getProperty("spring.config.additional-location");
        InputStream inputStream = null;
        if (StringUtils.isNotBlank(path) && path.contains(FILE_PREFIX)) {
            String[] paths = path.split(",", -1);
            path = paths[paths.length - 1].substring(FILE_PREFIX.length());
            return getRelativePathResource(path, "application.properties");
        }
        return null;
    }

    private static Resource getRelativePathResource(String parentPath, String path) {
        try {
            InputStream inputStream = new FileInputStream(Paths.get(parentPath, path).toFile());
            return new InputStreamResource(inputStream);
        } catch (Exception ignore) {
        }
        return null;
    }

    private static Resource getDefaultResource() {
        InputStream inputStream = EnvUtil.class.getResourceAsStream("/application.properties");
        return new InputStreamResource(inputStream);
    }

}
